صفحة الطابعات


محتويات الفولدر
| الفايلات |
|---|
| columns.ts |
| usePrinters.ts (هذا راح تلكاه هنا : src=> shared=> hooks=> usePrinters.tsx ) |
| PrintersView.tsx |
| JupiterServicesRequest.ts |
| useJupiterServices.ts |
| JupiterServicesView.tsx |
| PrinterDashboard.tsx |
columns.ts
export const getColumns = (t: TFunction): ColumnDef<Printer>[] => [
{ accessorKey: "name", header: t("name") },
{
accessorKey: "ipAddress",
header: t("pages.printers.columns.ipAddress"),
},
{
accessorKey: "port",
header: t("pages.printers.columns.port"),
},
];
- هذا الكود هو السكيما مال عرض التيبل الموجود بالتاب الثانية البيانات مالتها والي معروض بالصورة الفوك بطاقة كمثال عليها
usePrinters.ts
export interface Printer {
name: string;
ipAddress: string;
port: string;
}
export const getPrinters = async (): Promise<Printer[]> => {
const response = await axiosInstance.get("/printers");
return response.data;
};
export const getPrintersQuery = () => {
return useQuery<Printer[]>({
queryKey: ["printers"],
queryFn: getPrinters,
});
};
const usePrinters = () => {
const { data: printers } = getPrintersQuery();
return {
printers: printers || [],
};
};
export default usePrinters;
interface Printer: هذا راح يعرف انواع البيانات الي راح يتم استقبالهاgetPrinters: هذا هو ال Api Endpoint with get Requst Action الي هو مدموج هنا بملف الهووكgetPrintersQuery: هذا راح يسوي Fetch & Caching للطابعات- ثم اخير شي راح ننفذ الدالة ونرجع البيانات مالتنة
PrintersView.tsx
function PrintersView() {
const { printers } = usePrinters();
const { t, i18n } = useTranslation();
const columns = useMemo(() => getColumns(t), [i18n.language]);
return (
<div className="px-2 py-1 w-full space-y-3">
<DataTable
id="printers"
columns={columns}
data={printers}
headerActions={() => <></>}
/>
</div>
);
}
const { printers } = usePrinters();: هذا راح يجيب الطابعات الي اللوجك مالتها انشرح فوك وهاي بس النتيجة مالتهاconst columns = useMemo(() => getColumns(t), [i18n.language]);: هذا راح يجيب الاعمدة الي شرحناها بالفايل الاول فوك وراح يسوي Re-render كل ما تتغير اللغة- راح يستخدم ال DataTable Component والي راح تعرض الداتا بالشكل الي شفتها بالصورة وطبعا راح يتم شرح ال Component بالملفات المشتركة
- هذا الفايل راح يعرض التيبل مو الكاردات فقط للتنويه
JupiterServicesRequest.ts
export const baseSchema = (t: TFunction) =>
z.object({
name: z.string({ required_error: "الحقل مطلوب" }),
ipAddress: z.string({ required_error: "الحقل مطلوب" }),
port: z.string({ required_error: "الحقل مطلوب" }),
});
- هذا راح يكون مسؤول عن Validation الحقول مال اضافة خادم طباعة جديدة وراح يكونن كلهن اجباريات
useJupiterServices.ts
export const getJupiterServicesQuery = () => {
return useQuery<JupiterService[]>({
queryKey: ["jupiter-services"],
queryFn: getJupiterServices,
});
};
- هنا راح يسوي get خادم طابعات وراح يرجع بيانات من نوع JupiterService والي تحوي :
- id
- name
- ipAddress
- port
- راح يسوي كاش للنتيجة ونوع البيانات
const {
register,
handleSubmit,
control,
reset,
formState: { errors },
} = useForm<JupiterServicesRequest>({
resolver: zodResolver(schema),
defaultValues: defaultValues,
});
- هنا هاي راح تاخذ السكيما الي شرحناها قبل شوية وراح تنفذ عليها دالة ال zodResolver والي راح ترجع النة مجموعة من الدوال المفيدة مثل ال (reset , control ...etc)
const addNewJupiterServiceMutation = useMutation({
mutationFn: createJupiterService,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["jupiter-services"],
});
setOpenDialog(false);
reset(defaultValues);
toast.success(
t("pages.management.jupiterServices.addJupiterServiceSuccess")
);
},
onError: (error: any) => {
toast.error(t("pages.management.jupiterServices.addJupiterServiceError"), {
description: error?.response?.data?.message,
});
},
});
// ------------------- //
const editJupiterServiceMutation = useMutation({
mutationFn: updateJupiterService,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["jupiter-services"],
});
setOpenDialog(false);
reset(defaultValues);
toast.success(
t("pages.management.jupiterServices.updateJupiterServiceSuccess")
);
},
onError: (error: any) => {
toast.error(
t("pages.management.jupiterServices.updateJupiterServiceError"),
{ description: error?.response?.data?.message }
);
},
});
// ------------------- //
const deleteJupiterServiceMutation = useMutation({
mutationFn: deleteJupiterService,
onSuccess: () => {
queryClient.invalidateQueries({
queryKey: ["jupiter-services"],
});
setOpenDialog(false);
reset(defaultValues);
toast.success(t("pages.management.jupiterServices.jupiterServiceDeleted"));
},
onError: (error: any) => {
toast.error(
t("pages.management.jupiterServices.jupiterServiceDeletedError"),
{ description: error?.response?.data?.message }
);
},
});
- هنا عندي 3 دوال كل وحدة راح تسوي عملية معينة مثل الاضافة , التعديل او الحذف
- من تنجح العملية مال اي وحدة من عدهن راح يصير Seamless Refetch لان الكاش القديم بعد ما يفيد لان البيانات صار عليها تحديث
- ايضا راح تصفر البيانات واي نافذة جانت مفتوحة سواء بالحذف او التعديل او الاضافة راح تغلق وتصفر قيم المدخلات ان وجدت
queryClient.invalidateQueries({queryKey: ["jupiter-services"]})هذا متكرر لان كاعد يصير Refetch على هذا ال Key
const openDeleteDialog = (id: string) => {
const jupiterService = jupiterServices?.find(
(jupiterService) => jupiterService.id === id
);
if (jupiterService) {
setSelectedJupiterService(jupiterService.id);
setDeleteDialog(true);
}
};
- هذا راح يجيك اول شي خادم الطابعة المختارة هل موجودة لو لا واذا موجودة راح يفتح النافذة مال تأكيد الحذف وراح يحط ال Id مالتها
const handleDelete = () => {
if (selectedJupiterService) {
deleteJupiterServiceMutation.mutate({ id: selectedJupiterService });
setDeleteDialog(false);
}
};
- هاي راح تنفذ عملية الحذف وراح تغلق نافذة التأكيد على الحذف همينة بنفس الوكت
const openAddDialog = () => {
setOpenDialog(true);
reset(defaultValues);
};
- هاي راح تفتح نافذة او الفورمة مال اضافة جديدة وتسوي تصفير للبيانات القديمة
const openEditDialog = (id: string) => {
const jupiterService = jupiterServices?.find(
(jupiterService) => jupiterService.id == id
);
if (jupiterService) {
setSelectedJupiterService(jupiterService.id);
setOpenDialog(true);
reset({
name: jupiterService.name,
ipAddress: jupiterService.ipAddress,
port: jupiterService.port,
});
}
};
- هاي راح تبحث على خادم الطابعة اذا موجودة او لا , واذا موجودة راح يحط ال Id مالتها وراح يفتح الفورمة الي هي نفس فورمة الاضافة بس الفرق هنا راح يحط البيانات الي جتي ك Default Values
- هنا استخدم ال Reset علمود يحط البيانات الحالية لخادم الطابعة
JupiterServicesView.tsx
const containerVariants = {
hidden: { opacity: 0 },
visible: { opacity: 1, transition: { staggerChldren: 0.1 } },
};
- هنا هذا خاص بالستايل مال الحاوية الرئيسية بالصفحة
- راح يكون مختفي وبعدين ينعرض ويكون فرق العرض بين كارد واللخ 0.1 والي هو staggerChldren: 0.1
const cardVariants = {
hidden: { opacity: 0, y: -50 },
visible: {
y: 0,
opacity: 1,
transition: { type: "spring", damping: 15, stiffness: 300 },
},
exit: {
scale: 0.95,
opacity: 0,
transition: {
duration: 0.2,
},
},
hover: {
y: -5,
boxShadow:
"0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05)",
borderColor: "var(--primary)",
transition: {
type: "spring",
damping: 10,
stiffness: 400,
},
},
};
cardVariants: هذا هم بيه مجموعة من الستايلات الي تخص الكارد نفسها مثل البوكس شادو والtransition وغيرها- طبعا الكل وحدة وحالتها يعني من يكون مختفي راح يكون الشفافية مالتة صفر وصاعد -50 ليفوك
- ومن راح يظهر راح يظهر ويكون بيه Effect من يظهر
- ايضا هذا يشمل ال hover , exit
- باقي الكود هو عبارة عن عرض للصفحة والكاردات وشوية لوجك يخص العرض والكود المشترك الي بيه راح يتم شرحة بفولدر الملفات المشتركة
PrinterDashboard.tsx
const [searchParams, setSearchParams] = useSearchParams();
const tabParam = searchParams.get("tab");
const [activeTab, setActiveTab] = React.useState<"jupiter" | "printer">(
tabParam == "printer" ? "printer" : "jupiter"
);
const handleTabChange = (value: "jupiter" | "printer") => {
setActiveTab(value);
const params = new URLSearchParams(searchParams);
params.set("tab", value);
setSearchParams(params);
};
useEffect(() => {
const tab = searchParams.get("tab");
setActiveTab(tab == "printer" ? "printer" : "jupiter");
}, [searchParams]);
- راح نستخدم هنا دالة ال useSearchParams الي كاعد نجيبها من ال react-router-dom والي راح تجيب قيمة ال tab= ?
- هذا راح يشوف يا Tab تم الضغط عليها وراح يغير القيمة بناء عليها
- ايضا بال handleTabChange راح يجيب ال params وراح يحط قيمة ال Tab حسب اليوزر وين ضغط على يا Tab
<Tabs
dir={dir as "rtl" | "ltr"}
defaultValue="jupiter"
value={activeTab}
onValueChange={(value) => handleTabChange(value as "jupiter" | "printer")}
className="w-full mt-4"
>
<TabsList dir={dir} className="grid w-full max-w-md grid-cols-2 mb-6">
<TabsTrigger dir={dir} value="jupiter">
{t("pages.printerDashboard.tabs.jupiter")}
</TabsTrigger>
<TabsTrigger dir={dir} value="printer">
{t("pages.printerDashboard.tabs.printers")}
</TabsTrigger>
</TabsList>
<AnimatePresence mode="wait">
{activeTab == "jupiter" && (
<motion.div
key="services"
initial="hidden"
animate="visible"
exit="exit"
variants={tabContentVariants}
className="w-full"
>
<JupiterServicesView />
</motion.div>
)}
{activeTab == "printer" && (
<motion.div
key="printers"
initial="hidden"
animate="visible"
exit="exit"
variants={tabContentVariants}
className="w-full"
>
<PrintersView />
</motion.div>
)}
</AnimatePresence>
</Tabs>
- هنا راح تشوف العرض مال الصفحة مال الطابعات والي بيها 2 Tabs والي كل Tab حيكون بيها وحدة من ال Component الي تم شرحها فوك وحسب شنو راح تضغط راح تظهر الك